///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Portions Copyright (c) GHI Electronics, LLC.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace GHIElectronics.NETMF.Net.Sockets
{
    using System;
    using System.IO;
    using System.Threading;

    public class NetworkStream : Stream
    {
        protected bool _disposed;
        private bool _ownsSocket;
        protected EndPoint _remoteEndPoint;
        internal Socket _socket;
        protected int _socketType;

        internal NetworkStream(Socket socket) : this(socket, false)
        {
        }

        internal NetworkStream(Socket socket, bool ownsSocket)
        {
            if (socket == null)
            {
                throw new ArgumentNullException();
            }
            try
            {
                this._remoteEndPoint = socket.RemoteEndPoint;
            }
            catch (Exception e)
            {
                //GetSocketOption is not supported
                //int errCode = (int) socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Error);
                //throw new IOException(errCode.ToString(), e);
                
                throw new IOException("Socket exception creating NetworkStream", e);
            }
            this._socket = socket;
            //GetSocketOption is not supported,set socket type to Stream 
            this._socketType = 1;//(int) this._socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Type);
            this._ownsSocket = ownsSocket;
        }

        public void Close(int timeout)
        {
            if (timeout < -1)
            {
                throw new ArgumentOutOfRangeException();
            }
            Thread.Sleep(timeout);
            this.Close();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (!this._disposed)
            {
                try
                {
                    if (disposing && this._ownsSocket)
                    {
                        this._socket.Close();
                        _socket = null;
                    }
                }
                finally
                {
                    this._disposed = true;
                }
            }
        }

        public override void Flush()
        {
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException();
            }
            //if (this._socket.m_Handle == -1)
            //{
            //    throw new IOException();
            //}
            if (buffer == null)
            {
                throw new ArgumentNullException();
            }
            if ((offset < 0) || (offset > buffer.Length))
            {
                throw new ArgumentOutOfRangeException();
            }
            if ((count < 0) || (count > (buffer.Length - offset)))
            {
                throw new ArgumentOutOfRangeException();
            }
            int available = this._socket.Available;
            if ((count > available) && (available > 0))
            {
                count = available;
            }

            return this._socket.Receive(buffer, offset, count, SocketFlags.None);

        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException();
            }
            //if (this._socket.m_Handle == -1)
            //{
            //    throw new IOException();
            //}
            if (buffer == null)
            {
                throw new ArgumentNullException();
            }
            if ((offset < 0) || (offset > buffer.Length))
            {
                throw new ArgumentOutOfRangeException();
            }
            if ((count < 0) || (count > (buffer.Length - offset)))
            {
                throw new ArgumentOutOfRangeException();
            }
            int bytesSent = 0;
            while (count > 0)
            {
                bytesSent = this._socket.Send(buffer, offset, count, SocketFlags.None);
                count -= bytesSent;
                offset += bytesSent;

                if(bytesSent == 0 && count>0)
                    throw new IOException();

            }
        }

        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        public override bool CanSeek
        {
            get
            {
                return false;
            }
        }

        public override bool CanTimeout
        {
            get
            {
                return true;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }

        public virtual bool DataAvailable
        {
            get
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException();
                }
                //if (this._socket.m_Handle == -1)
                //{
                //    throw new IOException();
                //}
                return (this._socket.Available > 0);
            }
        }

        public override long Length
        {
            get
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException();
                }
                //if (this._socket.m_Handle == -1)
                //{
                //    throw new IOException();
                //}
                return (long) this._socket.Available;
            }
        }

        public override long Position
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        public override int ReadTimeout
        {
            get
            {
                return this._socket.ReceiveTimeout;
            }
            set
            {
                if ((value == 0) || (value < -1))
                {
                    throw new ArgumentOutOfRangeException();
                }
                this._socket.ReceiveTimeout = value;
            }
        }

        public override int WriteTimeout
        {
            get
            {
                return this._socket.SendTimeout;
            }
            set
            {
                if ((value == 0) || (value < -1))
                {
                    throw new ArgumentOutOfRangeException();
                }
                this._socket.SendTimeout = value;
            }
        }
    }
}

